﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell.Flavor;
using Microsoft.VisualStudio.Shell.Interop;

namespace NTrace.VSPackage
{
    [ComVisible(true)]
    public abstract class NTraceProjectBase : FlavoredProjectBase, IVsBuildStatusCallback
    {
        private IList<Type> propertyPages;
        private IntPtr buildStatusCallbackCookie;
        private bool buildInProcess;

        public NTraceProjectBase()
        {
            this.propertyPages = new List<Type>();
            this.PropertyPages.Add(typeof(NTracePropertyPage));
        }
        
        public NTraceProjectBase GetProject()
        {
            return this;
        }

        internal NTracePackage Package { get; set; }

        /// <summary>
        /// Gets whether or not the project is being built.
        /// </summary>
        public bool BuildInProgress
        {
            get
            {
                return this.buildInProcess;
            }
        }

        #region IVsBuildStatusCallback Members

        public int BuildBegin(ref int pfContinue)
        {
            this.buildInProcess = true;
            return HResult.S_OK;
        }

        public int BuildEnd(int fSuccess)
        {
            this.buildInProcess = false;
            return HResult.S_OK;
        }

        public int Tick(ref int pfContinue)
        {
            return HResult.S_OK;
        }

        #endregion
        
        protected override void SetInnerProject(IntPtr innerIUnknown)
        {
            if (base.serviceProvider == null)
            {
                base.serviceProvider = this.Package;
            }

            base.SetInnerProject(innerIUnknown);
        }

        protected override void Close()
        {
            base.Close();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="itemId"></param>
        /// <param name="propId"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        /// <remarks>See Visual Studio SDK: "How to: Add and Remove Property Pages"</remarks>
        protected override int GetProperty(uint itemId, int propId, out object property)
        {
            EtwTrace.Trace(EtwTraceLevel.Verbose, EtwTraceFlag.StackTracing, "GetProperty - enter");

            try
            {
                int output;

                __VSHPROPID2 requestedProperty = (__VSHPROPID2)propId;
                switch (requestedProperty)
                {
                    case __VSHPROPID2.VSHPROPID_PropertyPagesCLSIDList:
                        // Get a semicolon-delimited list of clsids of the configuration-independent property pages.
                        EtwTrace.Trace(
                          EtwTraceLevel.Information,
                          EtwTraceFlag.Diagnostic,
                          "Requested property ID: VSHPROPID_PropertyPagesCLSIDList");

                        StringBuilder buffer = new StringBuilder();
                        Object baseProperties;
                        // First, get the list from our base class
                        EtwTrace.Trace(EtwTraceLevel.Information, EtwTraceFlag.Diagnostic, "Querying base for property pages");
                        output = base.GetProperty(itemId, propId, out baseProperties);

                        if (0 == output)
                        {
                            String basePropertiesAsString = baseProperties as String;
                            if (!String.IsNullOrEmpty(basePropertiesAsString))
                            {
                                buffer.Append((String)baseProperties);
                                buffer.Append(";");
                            }
                        }
                        else
                        {
                            System.ComponentModel.Win32Exception comException = new System.ComponentModel.Win32Exception(output);
                            EtwTrace.Trace(EtwTraceLevel.Error, EtwTraceFlag.Diagnostic, "Error encountered in GetProperty: {0}", comException);
                        }

                        Type propertyPageInterfaceType = typeof(IPropertyPage);

                        // IsSubclassOf can't be used to check for interface inheritance, so
                        // we must invert the condition to see if an instance of the type can 
                        // be assigned to an IPropertyPage variable
                        IEnumerable<String> propertyPageGuids = from p in this.PropertyPages
                                                                where p != null && propertyPageInterfaceType.IsAssignableFrom(this.PropertyPages[0])
                                                                select p.GUID.ToString("B").ToUpper();
                        buffer.Append(String.Join(";", propertyPageGuids.ToArray()));

                        property = buffer.ToString();
                        EtwTrace.Trace(EtwTraceLevel.Verbose, EtwTraceFlag.StackTracing, "Returning: {0}", property);
                        output = 0;
                        break;
                    default:
                        EtwTrace.Trace(EtwTraceLevel.Information, EtwTraceFlag.Diagnostic, "Requested property ID: {0}. Delegating to base class", propId);
                        output = base.GetProperty(itemId, propId, out property);
                        break;
                }

                return output;
            }
            finally
            {
                EtwTrace.Trace(
                  EtwTraceLevel.Verbose,
                  EtwTraceFlag.StackTracing,
                  "GetProperty - exit");
            }
        }

        /// <summary>
        /// Gets an <see cref="IList&lt;Type&gt;"/> that contains a list of types that implement property pages
        /// </summary>
        protected IList<Type> PropertyPages
        {
            get
            {
                return this.propertyPages;
            }
        }

        protected override void InitializeForOuter(string fileName, string location, string name, uint flags, ref Guid guidProject, out bool cancel)
        {
            base.InitializeForOuter(fileName, location, name, flags, ref guidProject, out cancel);
        }
    }
}
